package com.tsd.core.utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;

/**
 * @author Hillpool
 */
public class RSAUtil {
    String publicK;
    String privateK;

    private static final String PUBLIC_KEY = "RSAPublicKey";

    private static final String PRIVATE_KEY = "RSAPrivateKey";

    private static final String KEY_ALGORITHM = "RSA";

    /**
     * 从文件中加载密钥
     *
     * @param publicPath
     * @param privatePath
     */
    public void loadKey(String publicPath, String privatePath) {
        if (privatePath != null) {
            privateK = FileUtil.readToString(privatePath);
        }
        if (publicPath != null) {
            publicK = FileUtil.readToString(publicPath);
        }
    }

    /**
     * 设置公钥字符串，使用loadKey时则不适用set方法
     *
     * @param key
     */
    public void setPublicK(String key) {
        this.publicK = key;
    }

    /**
     * 设置私钥字符串，使用loadKey时则不适用set方法
     *
     * @param key
     */
    public void setPrivateK(String key) {
        this.privateK = key;
    }

    /**
     * 当长度过长的时候，需要分割后加密 117个字节
     */
    private byte[] getMaxResultEncrypt(String str, Cipher cipher) throws Exception {
        try {
            // 最大加密字节数，超出最大字节数需要分组加密
            int maxEncryptBlock = 117;
            byte[] input = str.getBytes(StandardCharsets.UTF_8);
            byte[] resultBytes = doEncrypt(input, cipher, maxEncryptBlock);
            return resultBytes;
        } catch (Exception e) {
            throw new Exception("加密处理失败," + e.getMessage());
        }
    }

    /**
     * 当长度过长的时候，需要分割后解密 128个字节
     */
    private byte[] getMaxResultDecrypt(String str, Cipher cipher) throws Exception {
        try {
            // 最大解密字节数，超出最大字节数需要分组加密
            int maxEncryptBlock = 128;
            byte[] input = Base64.getDecoder().decode(str.getBytes(StandardCharsets.UTF_8));
            byte[] resultBytes = doEncrypt(input, cipher, maxEncryptBlock);
            return resultBytes;
        } catch (Exception e) {
            throw new Exception("解密数据处理异常，" + e.getMessage());
        }
    }

    private byte[] doEncrypt(byte[] inputArray, Cipher cipher, int maxEncryptBlock) throws UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
        byte[] resultBytes = {};
        int inputLength = inputArray.length;
        int offSet = 0;
        byte[] cache;
        while (inputLength - offSet > 0) {
            if (inputLength - offSet > maxEncryptBlock) {
                cache = cipher.doFinal(inputArray, offSet, maxEncryptBlock);
                offSet += maxEncryptBlock;
            } else {
                cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
                offSet = inputLength;
            }
            resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
            System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
        }
        return resultBytes;
    }

    /**
     * 使用RSA私钥加密
     *
     * @param data 加密数据
     */
    public String encryptByPrivateKey(String data) throws Exception {
        try {
            //base64编码的私钥
            byte[] decoded = Base64.getDecoder().decode(privateK);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(
                    new PKCS8EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            byte[] result = getMaxResultEncrypt(data, cipher);
            return new String(Base64.getEncoder().encode(result));
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("加密失败|" + e.getMessage());
        }
    }

    /**
     * 使用RSA私钥解密
     *
     * @param data 加密字符串
     */
    public String decryptByPrivateKey(String data) throws Exception {
        try {
            //base64编码的私钥
            byte[] decoded = Base64.getDecoder().decode(privateK);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(
                    new PKCS8EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            byte[] result = getMaxResultDecrypt(data, cipher);
            return new String(Base64.getEncoder().encode(result));
        } catch (Exception e) {
            throw new Exception("解密失败|" + e.getMessage());
        }
    }

    /**
     * 使用RSA公钥加密
     *
     * @param data 加密数据
     */
    public String encryptByPublicKey(String data) throws Exception {
        try {
            //base64编码的公钥
            byte[] decoded = Base64.getDecoder().decode(publicK);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(
                    new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] result = getMaxResultEncrypt(data, cipher);
            return new String(Base64.getEncoder().encode(result));
        } catch (Exception e) {
            throw new Exception("加密失败|" + e.getMessage());
        }
    }

    /**
     * 使用RSA私钥解密
     *
     * @param data 加密字符串
     */
    public String decryptByPublieKey(String data) throws Exception {
        try {
            //base64编码的公钥
            byte[] decoded = Base64.getDecoder().decode(publicK);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(
                    new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            byte[] result = getMaxResultDecrypt(data, cipher);
            return new String(Base64.getEncoder().encode(result));
        } catch (Exception e) {

            throw new Exception("解密失败|" + e.getMessage());
        }
    }
}
